<?php

namespace common\service;


use common\model\DelayOrderTaskModel;
use common\model\EntityModel;
use common\model\PlanModel;
use common\model\ProjectModel;
use common\model\ReviewFeedbackEntityModel;
use common\model\TaskClockModel;
use common\model\TaskMemberModel;
use common\model\TaskModel;
use common\model\TaskRelationTagModel;
use common\model\UserModel;
use support\SingletonTrait;
use think\Exception;
use think\Model;
use think\module\Module;

class TodoService
{
    use SingletonTrait;

    public function __construct()
    {
    }

    /**
     * 创建待办
     * @param $data
     * @return array|bool|null
     * @throws Exception
     */
    public function createTodo($data)
    {
        $commonService = CommonService::instance("task");
        $commonService->setCurrentModuleCode("task");
        $data['assignee'] = $data['executor'] = request()->getUserId();
        $data['entity_module_id'] = Module::$moduleDictData['module_index_by_code']['todo']['id'];
        if (empty($data['task_status'])) {
            $statusService = StatusService::getInstance();
            $taskDefaultStatus = $statusService->getTaskDefaultStatus();
            if (!empty($taskDefaultStatus)) {
                $data['task_status'] = $taskDefaultStatus['id'];
            }
        }
        return $commonService->create($data);
    }

    /**
     * 更新待办
     * @param $data
     * @return array
     * @throws Exception
     */
    public function updateTodo($data)
    {
        $commonService = CommonService::instance("task");
        $model = model(TaskModel::class);
        $oldData = $model->field("json")->find($data['id']);
        $oldJson = json_decode($oldData['json'], true);
        $data = $commonService->dealCreateCustomField($data, 'task');
        $data['json'] = array_merge($oldJson, $data['json'] ?? []);
        return $commonService->commonUpdate(["data" => $data]);
    }

    /**
     * 查询待办
     * @param $param
     * @return array
     * @throws \Exception
     */
    public function selectTodo($param)
    {
        $param['filter'] = append_filter_param($param, 'task', [
            "entity_module_id" => ["EQ", Module::$moduleDictData['module_index_by_code']['todo']['id']],
            "created_by" => ["EQ", request()->getUserId()],
        ]);

        $taskModel = model(TaskModel::class);
        return $taskModel->selectData($param);
    }

    /**
     * 按tag过滤task_id_list
     * @param $taskIdList
     * @param $filter
     * @return void
     */
    private function filterRelationTagForTask(&$taskIdList, $filter = null)
    {
        if (empty($taskIdList)) {
            return;
        }
        if (!empty($filter)) {
            $filter['task_id'] = ['IN', $taskIdList];
            $taskIdList = model(TaskRelationTagModel::class)
                ->field('task_id')
                ->where($filter)
                ->column('task_id');
            $taskIdList = array_unique($taskIdList);
        }
    }

    /**
     * 待办任务列表
     * @param $param
     * @param $filter
     * @param $page
     * @param int $userId
     * @param int $tenantId
     * @return array
     */
    public function taskList($param, $filter, $page, int $userId, int $tenantId)
    {
        // 为需求看板查询时，需要特殊过滤、排序
        $findForStageDeck = isset($filter['task.task_stage_id']);
        $tagFilter = [];
        if (isset($filter['task.episode_id'])) {
            $tagFilter = [
                'link_module_code' => 'episode',
                'link_id' => $filter['task.episode_id']
            ];
            unset($filter['task.episode_id']);
        }
        $res = [
            'upcoming' => [],
            'rows' => [],
            'total' => 0,
        ];
        $demandOrderId = $filter['demand_order_id'] ?? null;
        $delayOrderId = $filter['delay_order_id'] ?? null;
        $isLock = $filter['task_is_lock'] ?? null;
        unset($filter['demand_order_id'], $filter['delay_order_id'], $filter['task_is_lock']);
        //需求单id
        if (!empty($demandOrderId)) {
            $reviewFeedbackEntityIds = model(ReviewFeedbackEntityModel::class)
                ->where(['review_feedback_entity.review_feedback_id' => $demandOrderId])
                ->field("entity_id")
                ->column("entity_id");
            if (!empty($reviewFeedbackEntityIds)) {
                $reviewFeedbackEntityIds = array_unique($reviewFeedbackEntityIds);
                $filter['task.source_entity_id'] = ["IN", $reviewFeedbackEntityIds];
            } else {
                return $res;
            }
        }

        //延期单id
        if (!empty($delayOrderId)) {
            $delayOrderTaskIds = model(DelayOrderTaskModel::class)
                ->where(['delay_order_id' => $delayOrderId, 'type' => ['in', [DelayOrderTaskModel::TYPE_REQUEST_DELAY, DelayOrderTaskModel::TYPE_WORK_ORDER]] ])
                ->field("task_id")
                ->column("task_id");
            if (!empty($delayOrderTaskIds)) {
                $delayOrderTaskIds = array_unique($delayOrderTaskIds);
                $filter['task.id'] = ["IN", $delayOrderTaskIds];
            } else {
                return $res;
            }
        }

        if (!empty($isLock)) {
            $lockFilter = [
                ["task.is_lock" => $isLock],
            ];

            if ($isLock === "no") {
                $lockFilter[] = ["task.is_lock" => ['EXP', 'IS NULL']];
                $lockFilter["_logic"] = "OR";
            }
            $filter[] = $lockFilter;
        }

        $field = [
            "task.id",
            "task.name",
            "task.entity_id",
            "task.created_by",
            "task.description",
            "task.plan_start_time",
            "task.plan_end_time",
            "task.plan_delivery_time",
            "task.end_time",
            "task.project_id",
            "task.created",
            "task.created_by",
            "task.json",
            "task.step_type",
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_stage_id')) AS stage_id", // 阶段ID
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.is_sub_todo')) AS is_sub_todo", // 是否子待办
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.parent_todo_task_id')) AS parent_todo_task_id", // 主待办id
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.template_type')) AS template_type",
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.executor_list')) AS executor_list" // 模板待办执行人list
        ];
        $statusService = StatusService::getInstance();
        $doneStatus = $statusService->getTaskDoneStatus();
        $closedStatus = $statusService->getTaskDefaultClosedStatus();

        $filter['task.tenant_id'] = $tenantId;
        $filter['task.entity_module_id'] = module_by_code('todo')['id'];
        $filter = parse_filter_to_tp_where($filter, 'task');

        if ($findForStageDeck) {
            $filter['task.json->>"$.task_status"'] = ['NEQ', $closedStatus['id']];
        }

        if (empty($tagFilter)) {
            $res['total'] = model(TaskModel::class)
                ->join("task_member on task.id = task_member.task_id")
                ->where($filter)
                ->count('distinct task.id');
        } else {
            $taskIdList = model(TaskModel::class)
                ->join("task_member on task.id = task_member.task_id")
                ->where($filter)
                ->field('task.id')
                ->column('id');
            $this->filterRelationTagForTask($taskIdList, $tagFilter);
            if ($findForStageDeck && !empty($taskIdList)) {
                // 查询未完成任务 计划完成时间
                $timelineFilter = [
                    'task.json->>"$.task_status"' => ['NOT IN', [$closedStatus['id'], $doneStatus['id']]],
                    'task.id' => ['IN', $taskIdList],
                ];
                $upcomingSql = ' if(plan_end_time is null or plan_end_time=0, 9999999999, cast(plan_end_time AS SIGNED)) asc ';
                $upcoming = model(TaskModel::class)
                    ->where($timelineFilter)
                    ->field("plan_end_time")
                    ->order("ORDER_STRING_REPLACEMENT")
                    ->fetchSql()->select();
                if (strpos($upcoming, 'ORDER_STRING_REPLACEMENT')) {
                    $upcoming = str_replace('`ORDER_STRING_REPLACEMENT`', $upcomingSql, $upcoming);
                }
                if (strpos($upcoming, 'ORDER_STRING_REPLACEMENT')) {
                    $upcoming = str_replace('ORDER_STRING_REPLACEMENT', $upcomingSql, $upcoming);
                }
                $res['upcoming'] = array_column(model(\think\Model::class)->query($upcoming), 'plan_end_time');
            }
            $res['total'] = count($taskIdList);
        }
        if ($res['total'] == 0) {
            return $res;
        }

        $stageOrderSql = ' if (json->>"$.task_status"=' . $doneStatus['id'] . ',
                    end_time,
                    0 - if(plan_end_time is null or plan_end_time=0, 9999999999, cast(plan_end_time AS SIGNED))
                ) desc ';

        if (!$findForStageDeck) {
            $taskIdList = model(TaskModel::class)
                ->join("task_member on task.id = task_member.task_id")
                ->where($filter)
                ->order("task.created DESC")
                ->page($page[0], $page[1])
                ->group('task.id')
                ->field('task.id')
                ->column('id');

        } else {
            $taskIdList = model(TaskModel::class)
                ->join("task_member on task.id = task_member.task_id")
                ->where($filter)
                ->page($page[0], $page[1])
                ->group('task.id')
                ->field('task.id')
                ->order("ORDER_STRING_REPLACEMENT")
                ->fetchSql()->select();
            if (strpos($taskIdList, 'ORDER_STRING_REPLACEMENT')) {
                $taskIdList = str_replace('`ORDER_STRING_REPLACEMENT`', $stageOrderSql, $taskIdList);
            }
            if (strpos($taskIdList, 'ORDER_STRING_REPLACEMENT')) {
                $taskIdList = str_replace('ORDER_STRING_REPLACEMENT', $stageOrderSql, $taskIdList);
            }
            $taskIdList = array_column(model(\think\Model::class)->query($taskIdList), 'id');
        }
        if (empty($taskIdList)) {
            return $res;
        }

        $this->filterRelationTagForTask($taskIdList, $tagFilter);
        if (empty($taskIdList)) {
            return $res;
        }

        $rows = model(TaskModel::class)
            ->field($field)
            ->where(['task.id' => ['IN', $taskIdList]])
            ->order("task.created DESC")
            ->select();
        if (empty($rows)) {
            return $res;
        }

        $taskDelayMap = ReviewTaskDelayService::getInstance()->taskDelayInfo($taskIdList);
        $taskStatusList = StatusService::getInstance()->getList([], "*");
        $taskStatusList = array_column($taskStatusList, null, 'id');
        $projectIdList = [];
        $userIdList = [];
        foreach ($rows as &$row) {
            $taskJson = json_decode($row['json'], true);
            unset($row['json']);
            $row = array_merge($row, $taskJson);
            $taskIdList[] = $row['id'];
            if ($row['project_id'] > 0) {
                $projectIdList[$row['project_id']] = $row['project_id'];
            }

            if ($row['created_by'] > 0) {
                $userIdList[$row['created_by']] = $row['created_by'];
            }
            $row['is_sub_todo'] = !empty($row['is_sub_todo']);
            // 是否申请延期
            $row['is_task_delay'] = false;
            // 延期时间
            $row['task_delay_time'] = 0;
            $row['is_task_delay'] = !empty($taskDelayMap[$row['id']]);
            $row['task_delay_time'] = $taskDelayMap[$row['id']]['task_delay_time'] ?? 0;
            $row['task_delay_reason'] = $taskDelayMap[$row['id']]['task_delay_reason'] ?? null;
            $row = ['task' => $row, 'task_status' => null];
            if (!empty($taskJson['task_status'])) {
                $row['task_status'] = $taskStatusList[$taskJson['task_status']] ?? null;
            }
            // 文件
            $row['file_list'] = [];
            if (!empty($taskJson['template_todo_info']['file_list'])) {
                $row['file_list'] = $taskJson['template_todo_info']['file_list'];
                $deliveryList = TaskMemberService::getInstance()->getDeliveryCurrent($row['file_list'], null, $row['task']['id']);
                if (!empty($deliveryList['number'])) {
                    $row['file_list'] = $deliveryList['delivery_list'];
                    unset($deliveryList['delivery_list']);
                    $row['file_list_version'] = $deliveryList;
                }
            }
        }

        // 阶段搜索时排序
        if ($findForStageDeck) {
            usort($rows, function ($a, $b) use ($doneStatus) {
                // 均完成 【最新的已完成 (最大)】靠前
                if (($a['task_status']['id'] == $doneStatus['id']) && ($b['task_status']['id'] == $doneStatus['id'])) {
                    if ($a['task']['end_time'] > $b['task']['end_time']) {
                        return -1;
                    } else return 1;
                }
                // 均未完成 【最近的未完成 (最小)】靠前
                if (($a['task_status']['id'] != $doneStatus['id']) && ($b['task_status']['id'] != $doneStatus['id'])) {
                    if (empty($a['task']['plan_end_time'])) {
                        return 1;
                    }
                    if (empty($b['task']['plan_end_time'])) {
                        return -1;
                    }
                    if ($a['task']['plan_end_time'] < $b['task']['plan_end_time']) {
                        return -1;
                    } else return 1;
                }
                // 其他 【已完成】 靠前
                if ($a['task_status']['id'] == $doneStatus['id']) {
                    return -1;
                }
                return 1;
            });
        }

        // 获取同个任务下的其他参与者ID
        $taskMemberList = model(TaskMemberModel::class)->field('id,task_id,user_id,partner,status')->where(['task_id' => ['in', implode(',', $taskIdList)]])->select();
        foreach ($taskMemberList as $taskMember) {
            if ($taskMember['partner'] > 0) {
                $userIdList[$taskMember['partner']] = $taskMember['partner'];
            }
        }
        $taskMemberList = array_group_by($taskMemberList, 'task_id');
        $userList = [];
        if (!empty($userIdList)) {
            $userList = model(UserModel::class)->field('id,name,avatar')->where(['id' => ['in', implode(',', $userIdList)]])->select();
            $userList = array_column($userList, null, 'id');
        }
        // 项目信息
        $projectList = [];
        if (!empty($projectIdList)) {
            $projectList = model(ProjectModel::class)->where(['id' => ['IN', implode(',', $projectIdList)]])->field('id,name,code')->select();
            $projectList = array_column($projectList, null, 'id');
        }
        // 获取提醒时间
        $taskClockModel = model(TaskClockModel::class);
        $taskClockList = $taskClockModel->field('task_id,time')->where(['task_id' => ['IN', $taskIdList], ['type' => 'to_do']])->select();
        $taskClockList = array_group_by($taskClockList, 'task_id');
        // 排期信息查询
        $planList = model(PlanModel::class)->where(['task_id' => ['IN', $taskIdList]])->select();
        $planList = array_column($planList, null, 'task_id');

        // 获取模板发起人,执行人
        //        $templateMemberMap = TemplateTodoService::getInstance()->getMemberList(join(',', $taskIdList));

        $planDeliverTimeData = $this->getPlanDeliveryTime($taskIdList);

        // 获取状态ID
        $currentTime = time();
        foreach ($rows as &$row) {
            $row['task_status']['is_overtime'] = false;
            if (!empty($row['task_status'])) {
                if ($row['task_status']['id'] != $doneStatus['id']) {
                    $row['task_status']['is_overtime'] = $row['task']['plan_end_time'] > $currentTime;
                }
            }

            $taskId = $row['task']['id'];
            $row['todo_time'] = $taskClockList[$taskId] ?? [];
            $row['status'] = null;
            $row['member_self'] = null;
            $row['project'] = $projectList[$row['task']['project_id']] ?? null;
            $row['task_member_list'] = $taskMemberList[$taskId] ?? [];
            $row['plan'] = $planList[$taskId] ?? null;
            $row['delivery_plan'] = $planDeliverTimeData[$taskId] ?? null;

            foreach ($row['task_member_list'] as $taskMemberIndex => &$taskMember) {
                $taskMember['user_info'] = null;
                if ($taskMember['partner'] > 0) {
                    $taskMember['user_info'] = $userList[$taskMember['partner']] ?? null;
                }
                $taskMember['status_info'] = $taskStatusList[$taskMember['status']] ?? null;
                if ($taskMember['partner'] == $userId) {
                    $row['member_self'] = $row['task_member_list'][$taskMemberIndex];
                    $row['status'] = $taskStatusList[$taskMember['status']] ?? null;
                }
            }
            //            $row['starter_list'] = $templateMemberMap[$row['task']['id']]['starter_list'] ?? [];
            //            $row['executor_list'] = $templateMemberMap[$row['task']['id']]['executor_list'] ?? [];
            $row['starter_list'] = []; // 事项待办没有发起人,默认未空
            $row['executor_list'] = $row['task']['executor_list'] ?? []; // 事项待办执行人
        }
        $res['rows'] = $rows;
        return $res;
    }


    /**
     * 获取计划交付时间
     * @param $todTaskIds
     * @param array $episodeStepCategoryMap
     * @param array $taskTagMap
     * @return array
     */
    public function getPlanDeliveryTime($todTaskIds, $episodeStepCategoryMap = [], $taskTagMap = [])
    {
        $taskTagList = [];
        if (!empty($todTaskIds)) {
            $taskTagList = model(TaskRelationTagModel::class)->where(['task_id' => ["IN", $todTaskIds]])->select();
        }

        $taskTagGroup = [];
        foreach ($taskTagList as $taskTagItem) {
            if ($taskTagItem['link_module_code'] == 'episode') {
                $taskTagGroup[$taskTagItem['task_id']][$taskTagItem['link_module_code']][] = $taskTagItem['link_id'];
            } else {
                $taskTagGroup[$taskTagItem['task_id']]['step_category'][] = $taskTagItem['link_id'];
            }
        }

        //组合
        foreach ($taskTagGroup as $taskTagGroupIndex => $taskTagGroupItem) {
            if (!isset($taskTagGroupItem['episode']) || !isset($taskTagGroupItem['step_category'])) {
                continue;
            }
            foreach ($taskTagGroupItem['episode'] as $episodeId) {
                $episodeIds[$episodeId] = $episodeId;
                foreach ($taskTagGroupItem['step_category'] as $stepCategoryId) {
                    $taskTagMap[$taskTagGroupIndex][$episodeId . '#' . $stepCategoryId] = $episodeId . '#' . $stepCategoryId;
                }

                if (isset($episodeStepCategoryMap[$episodeId])) {
                    $episodeStepCategoryMap[$episodeId] = array_unique(array_merge($episodeStepCategoryMap[$episodeId], $taskTagGroupItem['step_category']));
                } else {
                    $episodeStepCategoryMap[$episodeId] = $taskTagGroupItem['step_category'];
                }
            }
        }

        $episodeStepCategoryWhere = [];
        if (!empty($episodeStepCategoryMap)) {
            foreach ($episodeStepCategoryMap as $episode => $episodeStepCategoryItem) {
                $episodeStepCategoryWhere[] = [
                    'plan.entity_id' => $episode,
                    's_tag.link_id' => ["IN", $episodeStepCategoryItem]
                ];
            }
            $episodeStepCategoryWhere['_logic'] = "OR";
            $episodeStepCategoryWhere = [$episodeStepCategoryWhere];
        }


        $episodeStepCategoryPlanMap = PlanService::getInstance()->getStepCategoryPlanData($episodeStepCategoryWhere);


        $taskDeliveryTimeMap = [];
        foreach ($taskTagMap as $taskId => $taskTagItem) {
            $intersectPlan = array_intersect_key($episodeStepCategoryPlanMap, $taskTagItem);
            $deliveryTime = null;
            if (!empty($intersectPlan)) {

                $min = 0;
                foreach ($intersectPlan as $intersectPlanItem) {
                    if ($min === 0 || ($intersectPlanItem['end_time'] > 0 && $intersectPlanItem['end_time'] < $min)) {
                        $min = $intersectPlanItem['end_time'];
                        $deliveryTime = $intersectPlanItem;
                    }
                }
            }
            $taskDeliveryTimeMap[$taskId] = $deliveryTime;
        }

        return $taskDeliveryTimeMap;
    }
}

